 class PSCamera extends Camera;

// Offset for the default third person camera
var float DefaultCamOffsetX;
var float DefaultCamOffsetY;
var float DefaultCamOffsetZ;

// Offset for the zoomed camera in third person view
var bool bAimActive;
var float AimOffsetX;
var float AimOffsetY;
var float AimOffsetZ;

// Offset for the crouch camera
var bool bCrouchActive;
var float CrouchOffsetX;
var float CrouchOffsetY;
var float CrouchOffsetZ;

// Offset for the zoomed camera in third person view when crouched
var bool bAimCrouchActive;
var float AimCrouchOffsetX;
var float AimCrouchOffsetY;
var float AimCrouchOffsetZ;

// Offset for the camera location when under cover
var bool bCover;
var Rotator rCoverOffset;
var float CoverOffsetX;
var float CoverOffsetY;
var float CoverOffsetZ;
var float CoverEdgeLeanOffset;
var int CoverEdgeLeanYaw;

// Offset for the camera location when carrying an object
var bool bCarryingObject;
var float CarryingZoomOffsetX;
var float CarryingZoomOffsetY;
var float CarryingZoomOffsetZ;

// Offset for the camera when colliding with the environment
var float CollisionCamOffsetY;

// Death camera offset
var bool bDeathCamera;
var float DeathOffsetX;
var float DeathOffsetY;
var float DeathOffsetZ;
var int DeathPitch;

// Flashback camera offset
var bool bFlashbackCamera;
var float FlashbackOffsetX;
var float FlashbackOffsetY;
var float FlashbackOffsetZ;
var int FlashbackPitch;
var int FlashbackYaw;

// Required for smooth camera movement
var Vector CurrentCamLocation;
var Vector DestinationCamLocation;
var Rotator CurrentCamRotation;
var Rotator DestinationCamRotation;

// FOV values
var float NotAimingFOV;
var float AimingFOV;
var float CurrentFOV;
var float DestinationFOV;

var bool bPlayingCinematic;

var bool bPlayerHidden;


function UpdateViewTarget(out TViewTarget OutVT, float DeltaTime)
{
	local Pawn P;
    local Vector X,Y,Z;
	local Rotator R;
	local Vector HitLocation;
	local Vector HitNormal;
	local Vector HitStartLocation;
	local float Radius, Height;
	local Vector XComponent, YComponent;
	local Vector LeanVector;
	local Actor HitActor;

	P = PSPawn(OutVT.Target);

	// In case the camera target is not the pawn, let Matinee play the cinematic
	if(P == none)
	{
		super.UpdateViewTarget(OutVT, DeltaTime);
		bPlayingCinematic = true;
		
		// Disable zoom if enabled and hide the crosshair accordingly
		if(IsAimEnabled())
		{
			PSPlayerController(PCOwner).CameraZoom();
		}

		return;
	}

    P.GetAxes(DestinationCamRotation, X, Y, Z);

	//Get the pawn's height as a base for the Z offset.
    P.GetBoundingCylinder(Radius, Height); 

	CurrentFOV = GetFOVAngle();

	// Set default FOV
	DestinationFOV = NotAimingFOV;

	// Aim camera mode in standing position
    if (bAimActive)
    {
    	DestinationCamLocation = P.Location + AimOffsetX * X + AimOffsetY * Y + (Height + AimOffsetZ) * Z;

		// Set aiming FOV
		DestinationFOV = AimingFOV;
    }
	// Aim camera mode in crouched position
    else if (bAimCrouchActive)
    {
    	DestinationCamLocation = P.Location + AimCrouchOffsetX * X + AimCrouchOffsetY * Y + (Height + AimCrouchOffsetZ) * Z;

		// Set aiming FOV
		DestinationFOV = AimingFOV;
    }
	// Carrying object camera mode
	else if(bCarryingObject)
	{
		DestinationCamLocation = P.Location + CarryingZoomOffsetX * X + CarryingZoomOffsetY * Y + (Height + CarryingZoomOffsetZ) * Z;
	}
	else if(bCover)
	{
		DestinationCamRotation = P.Rotation;

		XComponent = Normal(Vector(P.Rotation)).X * vect(-1.0, 0.0, 0.0);
		YComponent = Normal(Vector(P.Rotation)).Y * vect(0.0, -1.0, 0.0);

		// If the player is in a wall edge, then add a lean offset to the camera
		// Chech this to understand about how to manage vectors in this case: http://www.wikihow.com/Find-Perpendicular-Vectors-in-2-Dimensions
		if(PSPlayerController(PCOwner).cover.IsFullWall && PSPlayerController(PCOwner).cover.bOnEdge)
		{
			// The player is on the right edge of a wall
			if(PSPlayerController(PCOwner).cover.bOnEdgeRight)
			{
				LeanVector.X = YComponent.Y;
				LeanVector.Y = -XComponent.X;

				DestinationCamRotation.Yaw += CoverEdgeLeanYaw * DegToUnrRot;
			}
			// The player is on the left edge of a wall
			else
			{
				LeanVector.X = -YComponent.Y;
				LeanVector.Y = XComponent.X;

				DestinationCamRotation.Yaw -= CoverEdgeLeanYaw * DegToUnrRot;
			}

			DestinationCamLocation = P.Location + 
				(abs(CoverOffsetX) + abs(CoverEdgeLeanOffset)) * XComponent + 
				(abs(CoverOffsetY) + abs(CoverEdgeLeanOffset)) * YComponent + 
				abs(CoverEdgeLeanOffset) * LeanVector +
				(Height + CoverOffsetZ) * vect(0,0,1);
		}
		else
		{
			DestinationCamLocation = P.Location + 
				abs(CoverOffsetX) * XComponent + 
				abs(CoverOffsetY) * YComponent + 
				(Height + CoverOffsetZ) * vect(0,0,1);
		}
		
		if(PSPawnDidrik(P).CurrentPosture == PSPawnDidrik(P).EPosture.POSTURE_Covered_Crouch)
		{
			// Hardcodeeeeeeeeeeeeed yeaaaaaaaaaa!!!!
			DestinationCamLocation.Z += 52;
		}
	}
	// Flashback camera
	else if(bFlashbackCamera)
	{
		DestinationCamRotation.Pitch += FlashbackPitch*DegToUnrRot;
		DestinationCamRotation.Yaw += FlashbackYaw*DegToUnrRot;

		DestinationCamLocation = P.Location + FlashbackOffsetX * X + FlashbackOffsetY * Y + (Height + FlashbackOffsetZ) * Z;
	}
	// Death camera
	else if (bDeathCamera)
	{
		DestinationCamRotation.Pitch = DeathPitch*DegToUnrRot;
		DestinationCamLocation = P.Location + DeathOffsetX * X + DeathOffsetY * Y + (Height + DeathOffsetZ) * Z;	
	}
	// Crouch camera
	else if (bCrouchActive)
	{
		DestinationCamLocation = P.Location + CrouchOffsetX * X + CrouchOffsetY * Y + (Height + CrouchOffsetZ) * Z;
	}
	// Default camera mode
	else
	{
		DestinationCamLocation = P.Location + DefaultCamOffsetX * X + DefaultCamOffsetY * Y + (Height + DefaultCamOffsetZ) * Z;		
	}
	
	// Handle smooth transitions only if cinematic mode has not been just left
	if(!bPlayingCinematic)
	{
		// Smooth location transition
		if (DestinationCamLocation != CurrentCamLocation)
		{
			if(bAimActive || bAimCrouchActive)
			{
				CurrentCamLocation = VInterpTo(CurrentCamLocation, DestinationCamLocation, DeltaTime, 20);						
			}
			else if(bCover && PSPlayerController(PCOwner).cover.bOnEdge)
			{
				CurrentCamLocation = VInterpTo(CurrentCamLocation, DestinationCamLocation, DeltaTime, 2);
			}
			else if(bFlashbackCamera)
			{
				CurrentCamLocation = VInterpTo(CurrentCamLocation, DestinationCamLocation, DeltaTime, 0.5);
			}
			else
			{
				CurrentCamLocation = VInterpTo(CurrentCamLocation, DestinationCamLocation, DeltaTime, 15);			
			}
		}

		// Smooth rotation transition
		if (DestinationCamRotation != CurrentCamRotation)
		{
			if(bAimActive || bAimCrouchActive)
			{
				CurrentCamRotation = DestinationCamRotation;
			}
			else if(bCover && (DestinationCamRotation != P.Rotation) )
			{
				CurrentCamRotation = RInterpTo(CurrentCamRotation, DestinationCamRotation, DeltaTime, 2);
			}
			else if(bFlashbackCamera)
			{
				CurrentCamRotation = RInterpTo(CurrentCamRotation, DestinationCamRotation, DeltaTime, 0.5);
			}
			else
			{
				CurrentCamRotation = RInterpTo(CurrentCamRotation, DestinationCamRotation, DeltaTime, 10);
			}		
		}

		// Smooth FOV transition
		if (DestinationFOV != CurrentFOV)
		{
			if(bAimActive || bAimCrouchActive)
			{
				CurrentFOV = FInterpTo(CurrentFOV, DestinationFOV, DeltaTime, 20.0);
			}
			else
			{
				CurrentFOV = FInterpTo(CurrentFOV, DestinationFOV, DeltaTime, 5.0);
			}
		
			SetFOV(CurrentFOV);
		}
	}
	// If coming from cinematic mode, avoid smooth camera transitions
	else
	{
		CurrentCamLocation = DestinationCamLocation;
		CurrentCamRotation = DestinationCamRotation;
		CurrentFOV = DestinationFOV;

		bPlayingCinematic = false;
	}
	

	// Handle environment collisions
	P.GetActorEyesViewPoint(HitStartLocation, R);
		
	HitActor = Trace(HitLocation, HitNormal, CurrentCamLocation, HitStartLocation, true, vect(12,12,12), , TRACEFLAG_Blocking);
	if(HitActor != None)
	{
		CurrentCamLocation = HitLocation;

		if( (VSize(HitLocation - HitStartLocation) <= 30.0) && !bPlayerHidden)
		{
			P.Mesh.SetHidden(true);

			if(P.Weapon != none)
			{
				P.Weapon.Mesh.SetHidden(true);
			}

			bPlayerHidden = true;
		}
		else if( (VSize(HitLocation - HitStartLocation) > 40.0) && bPlayerHidden)
		{
			P.Mesh.SetHidden(false);

			if(P.Weapon != none)
			{
				P.Weapon.Mesh.SetHidden(false);
			}

			bPlayerHidden = false;
		}	
	}
	else if( (HitActor == none) && bPlayerHidden)
	{
		P.Mesh.SetHidden(false);

		if(P.Weapon != none)
		{
			P.Weapon.Mesh.SetHidden(false);
		}

		bPlayerHidden = false;
	}
	
	// Set current values
	OutVT.POV.Location = CurrentCamLocation;
    OutVT.POV.Rotation = CurrentCamRotation;

	//`log(CurrentCamLocation);
	//`log(CurrentCamRotation.Pitch*UnrRotToRad*RadToDeg @ CurrentCamRotation.Roll*UnrRotToRad*RadToDeg @ CurrentCamRotation.Yaw*UnrRotToRad*RadToDeg);
}

function CheckWeaponToBeHidden(Pawn P)
{
	if( (P.Weapon != none) && bPlayerHidden)
	{
		P.Weapon.Mesh.SetHidden(true);
	}
}

function ProcessViewRotation(float DeltaTime, out rotator OutViewRotation, out Rotator OutDeltaRot)
{
	DestinationCamRotation = OutViewRotation + OutDeltaRot;
}

// Toggle aim camera zoom
function AimZoom()
{
	bAimActive = !bAimActive;
}

function bool IsAimEnabled()
{
	return bAimActive;
}

function bool IsPlayingCinematic()
{
	return bPlayingCinematic;
}

defaultproperties
{
    DefaultCamOffsetX=-64.0
    DefaultCamOffsetY=24.0
    DefaultCamOffsetZ=-18.0

	bAimActive=false
	AimOffsetX=-36.0  
	AimOffsetY=24.0
	AimOffsetZ=-18.0

	bCrouchActive=false
	CrouchOffsetX=-96.0  
	CrouchOffsetY=28.0
	CrouchOffsetZ=0.0

	bAimCrouchActive=false
	AimCrouchOffsetX=-32.0  
	AimCrouchOffsetY=28.0
	AimCrouchOffsetZ=10.0

	bCover=false;
	CoverOffsetX=-128.0
	CoverOffsetY=-128.0
	CoverOffsetZ=-28.0
	CoverEdgeLeanOffset=42.0
	CoverEdgeLeanYaw=15

	bCarryingObject=false
	CarryingZoomOffsetX=-200.0
	CarryingZoomOffsetY=0.0
	CarryingZoomOffsetZ=26.0

	bDeathCamera=false
	DeathOffsetX=-128.0
	DeathOffsetY=0.0
	DeathOffsetZ=26.0
	DeathPitch=-40

	CollisionCamOffsetY=18;

	bFlashbackCamera=false
	FlashbackOffsetX=-128.0
	FlashbackOffsetY=56.0
    FlashbackOffsetZ=-10.0
    FlashbackPitch=-10.0
    FlashbackYaw=10.0

	NotAimingFOV=90.0
	AimingFOV=60.0

	bPlayingCinematic=false

	bPlayerHidden=false
}